<?php
/** @noinspection PhpPropertyCanBeReadonlyInspection */
/** @noinspection PhpPropertyCanBeReadonlyInspection */

declare(strict_types=1);

namespace Dreamcat\ApiDoc\ApiSaver;

use Dreamcat\ApiDoc\DocSaverInterface;
use Dreamcat\ApiDoc\Enums\SwaggerJsonMode;
use Dreamcat\ApiDoc\Enums\YapiMergeMode;
use Dreamcat\ApiDoc\Error\SaverException;
use Dreamcat\ApiDoc\Popo\ApiDefinition;
use Dreamcat\ApiDoc\Swagger\Define2Swagger;
use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\StreamFactory;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

/**
 * 将文档更新保存到yapi的保存器
 * @author vijay
 */
class YapiSaver implements DocSaverInterface
{
    /** @var string 导入数据的url */
    private string $url;
    /** @var Define2Swagger api定义转swagger定义的工具 */
    private Define2Swagger $define2Swagger;
    /** @var YapiMergeMode 数据同步方式 */
    private YapiMergeMode $mergeMode;
    /** @var LoggerInterface 日志记录器 */
    private LoggerInterface $logger;

    /**
     * @param string $yapiUrl yapi基准地址，类似于 http://yapi.baidu.com/
     * @param string $token 项目token
     * @param ClientInterface $client http客户端
     * @param ?YapiMergeMode $mergeMode 数据同步方式
     * @param string $basePath 基准路径
     */
    public function __construct(
        string $yapiUrl,
        private string $token,
        private ClientInterface $client,
        YapiMergeMode $mergeMode = null,
        private string $basePath = "/"
    ) {
        $this->url = rtrim($yapiUrl, "/") . "/api/open/import_data";
        if ($mergeMode) {
            $this->mergeMode = $mergeMode;
        } else {
            $this->mergeMode = YapiMergeMode::MERGE();
        }
    }

    /**
     * @return string 基准路径
     */
    public function getBasePath(): string
    {
        return $this->basePath;
    }

    /**
     * @param string $basePath 基准路径
     */
    public function setBasePath(string $basePath): void
    {
        $this->basePath = $basePath;
    }

    /**
     * @inheritDoc
     */
    public function start(): void
    {
        $this->define2Swagger = new Define2Swagger("yapi-saver");
        $this->define2Swagger->setSwaggerJsonMode(SwaggerJsonMode::YAPI())->setBasePath($this->basePath);
    }

    /**
     * @inheritDoc
     */
    public function saveApiDefinition(ApiDefinition $apiDefinition): void
    {
        $this->define2Swagger->addApiDefine($apiDefinition);
    }

    /**
     * @inheritDoc
     */
    public function end(): void
    {
        $data = [
            "type" => "swagger",
            "json" => json_encode($this->define2Swagger->getJson()),
            "merge" => $this->mergeMode->getValue(),
            "token" => $this->token,
        ];
        $request = (new RequestFactory())->createRequest("POST", $this->url);
        $this->getLogger()->info("准备发送请求到yapi {$this->url}");
        $body = http_build_query($data);
        $request = $request->withBody((new StreamFactory())->createStream($body))
            ->withHeader("Content-Type", "application/x-www-form-urlencoded");
        try {
            $response = $this->client->sendRequest($request);
            if ($response->getStatusCode() !== 200) {
                throw new SaverException(
                    "请求状态码 {$response->getStatusCode()} 与预期不符",
                    SaverException::UNEXPECT_HTTP_CODE
                );
            }
            $resBody = strval($response->getBody());
            if (!$resBody) {
                throw new SaverException("未得到响应体", SaverException::EMPTY_HTTP_BODY);
            }
            $this->getLogger()->info("yapi 请求结果 {$resBody}");
            $body = json_decode($resBody, true);
            $err = json_last_error();
            if ($err) {
                throw new SaverException(
                    "响应体json解析失败 {$err} " . json_last_error_msg(),
                    SaverException::INVALID_JSON_HTTP_BODY
                );
            }
            if ($body["errcode"]) {
                throw new SaverException("请求失败 msg = {$body["errmsg"]}", SaverException::INVALID_BUSI_CODE);
            }
            $this->getLogger()->info($body["errmsg"]);
        } catch (ClientExceptionInterface $e) {
            $this->getLogger()->error("请求失败 {$e->getMessage()}", [$e]);
            throw new SaverException($e->getMessage(), SaverException::HTTP_EXCEPTION, $e);
        }
    }

    /**
     * @return LoggerInterface 日志记录器
     */
    public function getLogger(): LoggerInterface
    {
        if (!isset($this->logger)) {
            $this->setLogger(new NullLogger());
        }
        return $this->logger;
    }

    /**
     * @param LoggerInterface $logger 日志记录器
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }
}

# end of file
